<template>
  <div ref="canvas"></div>
</template>

<script lang="ts" setup>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import Stats from "three/examples/jsm/libs/stats.module";
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
import { ref, onMounted } from "vue";

const canvas = ref();
// 场景
const scene = new THREE.Scene();
// 渲染器
const renderer = new THREE.WebGLRenderer({ antialias : false });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0x000000);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// 相机
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.set(20, 20, 150);
camera.lookAt(0, 0, 0);
// 相机控件
const orbitControl = new OrbitControls(camera, renderer.domElement);
// 性能统计
const stat = Stats();
// 粒子云

// gui
const controls = {
  size: 4,
  transparent: true, // 开启透明 - true: 粒子在渲染时会根据opacity设置粒子的不透明度(默认为1，完全不透明)
  opacity: 0.6, // 透明度 - 和opacity搭配使用。设置粒子的不透明度(默认为1，完全不透明)
  vertexColors: true, // 顶点染色
  color: 0xffffff, // 颜色
  vertexColor: 0x00ff00, // 顶点颜色
  sizeAttenuation: true,// 粒子尺寸衰减 - false: 所有粒子都拥有相同尺寸, true: 粒子的大小取决于其距离相机的远近
  rotate: true, // 旋转
  redraw: () => {
    if (scene.getObjectByName("particles")) {
      scene.remove(scene.getObjectByName("particles"));
    }
    createParticles(controls.size, controls.transparent, controls.opacity, controls.vertexColors,
        controls.sizeAttenuation, controls.color, controls.vertexColor);
  },// 重绘
};
const gui = new GUI();
// @ts-ignore
gui.add(controls, 'size', 0, 10).name("大小").step(1).onChange(controls.redraw);
// @ts-ignore
gui.add(controls, 'transparent').name("开启透明度").onChange(controls.redraw);
// @ts-ignore
gui.add(controls, 'opacity', 0, 1).name("透明度").step(0.01).onChange(controls.redraw);
// @ts-ignore
gui.add(controls, 'vertexColors').name("顶点染色").onChange(controls.redraw);
// @ts-ignore
gui.addColor(controls, 'color').name("颜色").onChange(controls.redraw);
// @ts-ignore
gui.addColor(controls, 'vertexColor').name("顶点颜色").onChange(controls.redraw);
// @ts-ignore
gui.add(controls, 'sizeAttenuation').name("粒子尺寸衰减").onChange(controls.redraw);
// @ts-ignore
gui.add(controls, 'rotate').name("开启旋转");

controls.redraw();

let step = 0.00;
const render = () => {
  stat.update();
  orbitControl.update();

  if (controls.rotate && scene.getObjectByName("particles")) {
    step += 0.01;
    const obj = scene.getObjectByName("particles");
    obj.rotation.x = step;
    obj.rotation.z = step;
  }

  requestAnimationFrame(render);
  renderer.render(scene, camera);
};

onMounted(() => {
  canvas.value.appendChild(stat.domElement);
  canvas.value.appendChild(renderer.domElement);

  render();
});

function createParticles(size, transparent, opacity, vertexColors, sizeAttenuation, colorValue, vertexColorValue) {
  const geom = new THREE.BufferGeometry();
  const material = new THREE.PointsMaterial({
    size: size,
    transparent: transparent,
    opacity: opacity,
    vertexColors: vertexColors,
    sizeAttenuation: sizeAttenuation,
    color: new THREE.Color(colorValue)
  });

  const points = [];
  const colors = [];
  const range = 500;
  for (let i = 0; i < 15000; i++) {
    points.push(
        Math.random() * range - range / 2,
        Math.random() * range - range / 2,
        Math.random() * range - range / 2
    );
    const color = new THREE.Color(vertexColorValue);
    const asHSL = { h : 0, s : 0, l : 0 };
    color.getHSL(asHSL);
    colors.push(asHSL.h, asHSL.s, asHSL.l * Math.random());
  }
  geom.setAttribute("position", new THREE.BufferAttribute(new Float32Array(points), 3));
  geom.setAttribute("color", new THREE.BufferAttribute(new Float32Array(colors), 3));

  const cloud = new THREE.Points(geom, material);
  cloud.name = "particles";
  scene.add(cloud);
}

</script>
